package com.dreampharos.util;

import java.io.*;
import java.net.*;
import java.util.*;

/**
 * HTTP ��û�� ����� ��, ������ �޾ƿ��� ��ƿ��Ƽ Ŭ����
 * <p>
 * GET ��İ� POST ������� �����͸� ������ָ�
 * POSTE ����� ��� multipart/form-date ���ڵ� ��ĵ�
 * �������ش�.
 *
 * @author �ֹ�� [era13@korea.com]
 */
public final class HttpRequestor {
    
    public static final String CRLF = "\r\n";
    
    /**
     * ������ URL
     */
    private URL targetURL;
    
    /**
     * �Ķ���� ����� �����ϰ� �ִ�.
     * �Ķ���� �̸��� ���� ���ʴ�� ����ȴ�.
     */
    private ArrayList list;
    
    /**
     * HttpRequest�� ���Ѵ�.
     * 
     * @param target HTTP �޽����� ����� ��� URL
     */
    public HttpRequestor(URL target) {
        this(target, 20);
    }
    
    /**
     * HttpRequest�� ���Ѵ�.
     * 
     * @param target HTTP �޽����� ����� ��� URL
     */
    public HttpRequestor(URL target, int initialCapicity) {
        this.targetURL = target;
        this.list = new ArrayList(initialCapicity);
    }
    
    /**
     * �Ķ���͸� �߰��Ѵ�.
     * @param parameterName �Ķ���� �̸�
     * @param parameterValue �Ķ���� ��
     * @exception IllegalArgumentException parameterValue�� null�� ���
     */
    public void addParameter(String parameterName, String parameterValue) {
        if (parameterValue == null) 
        throw new IllegalArgumentException("parameterValue can't be null!");
        
        list.add(parameterName);
        list.add(parameterValue);
    }
    
    /**
     * ���� �Ķ���͸� �߰��Ѵ�.
     * ���� parameterValue�� null�̸�(��, ����� ������ �������� �ʴ´ٸ�
     * ������ ��۵Ǵ� filename �� "" �� �ȴ�.
     * 
     * @param parameterName �Ķ���� �̸�
     * @param parameterValue ����� ����
     * @exception IllegalArgumentException parameterValue�� null�� ���
     */
    public void addFile(String parameterName, File parameterValue) {
        // paramterValue�� null�� ��� NullFile�� �����Ѵ�.
        if (parameterValue == null) {
            list.add(parameterName);
            list.add(new NullFile());
        } else {
            list.add(parameterName);
            list.add(parameterValue);
        }
    }
    /**
     * ���ݱ��� ������ �Ķ���͸� ��� �����Ѵ�.
     */
    public void clearParameters() {
        list.clear();
    }
    
    /**
     * GET ������� ��� URL�� �Ķ���͸� ����� ��
     * ������ InputStream���� �����Ѵ�.
     * @return InputStream
     */
    public InputStream sendGet() throws IOException {
        String paramString = null;
        if (list.size() > 0)
            paramString = "?" + encodeString(list);
        else
            paramString = "";
        
        URL url = new URL(targetURL.toExternalForm() + paramString);
        
        URLConnection conn = url.openConnection();
        
        return conn.getInputStream();
    }
    
    /**
     * POST ������� ��� URL�� �Ķ���͸� ����� ��
     * ������ InputStream���� �����Ѵ�.
     * @return InputStream
     */
    public InputStream sendPost() throws IOException {
        String paramString = null;
        if (list.size() > 0)
            paramString = encodeString(list);
        else
            paramString = "";
        
        HttpURLConnection conn = (HttpURLConnection)targetURL.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type",
                                "application/x-www-form-urlencoded");
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        
        DataOutputStream out = null;
        try {
            out = new DataOutputStream(conn.getOutputStream());
            out.writeBytes(paramString);
            out.flush();
        } finally {
            if (out != null) out.close();
        }
        return conn.getInputStream();
    }
    
    /**
     * multipart/form-data ���ڵ��� ����Ͽ�
     * ��� URL�� �����͸� ����� �Ŀ�
     * ������ InputStream���� �����Ѵ�.
     * @return InputStream
     */
    public InputStream sendMultipartPost() throws IOException {
        HttpURLConnection conn = (HttpURLConnection)targetURL.openConnection();
        
        // Delimeter ��
        String delimeter = makeDelimeter();
        
        byte[] newLineBytes = CRLF.getBytes();
        byte[] delimeterBytes = delimeter.getBytes();
        byte[] dispositionBytes = "Content-Disposition: form-data; name=".getBytes();
        byte[] quotationBytes = "\"".getBytes();
        byte[] contentTypeBytes = "Content-Type: application/octet-stream".getBytes();
        byte[] fileNameBytes = "; filename=".getBytes();
        byte[] twoDashBytes = "--".getBytes();
        
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type",
                                "multipart/form-data; boundary="+delimeter);
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        
        BufferedOutputStream out = null;
        try {
            out = new BufferedOutputStream(conn.getOutputStream());
            
            Object[] obj = new Object[list.size()];
            list.toArray(obj);
            
            for (int i = 0 ; i < obj.length ; i += 2) {
                // Delimeter ���
                out.write(twoDashBytes);
                out.write(delimeterBytes);
                out.write(newLineBytes);
                // �Ķ���� �̸� ���
                out.write(dispositionBytes);
                out.write(quotationBytes);
                out.write( ((String)obj[i]).getBytes() );
                out.write(quotationBytes);
                if ( obj[i+1] instanceof String) {
                    // String �̶��
                    out.write(newLineBytes);
                    out.write(newLineBytes);
                    // �� ���
                    out.write( ((String)obj[i+1]).getBytes() );
                    out.write(newLineBytes);
                } else {
                    // �Ķ������ ���� File �̳� NullFile�� ���
                    if ( obj[i+1] instanceof File) {
                        File file = (File)obj[i+1];
                        // File�� �����ϴ� �� �˻��Ѵ�.
                        out.write(fileNameBytes);
                        out.write(quotationBytes);
                        out.write(file.getAbsolutePath().getBytes() );
                        out.write(quotationBytes);
                    } else {
                        // NullFile �� ���
                        out.write(fileNameBytes);
                        out.write(quotationBytes);
                        out.write(quotationBytes);
                    }
                    out.write(newLineBytes);
                    out.write(contentTypeBytes);
                    out.write(newLineBytes);
                    out.write(newLineBytes);
                    // File �����͸� ����Ѵ�.
                    if (obj[i+1] instanceof File) {
                        File file = (File)obj[i+1];
                        // file�� �ִ� ������ ����Ѵ�.
                        BufferedInputStream is = null;
                        try {
                            is = new BufferedInputStream(
                                     new FileInputStream(file));
                            byte[] fileBuffer = new byte[1024 * 8]; // 8k
                            int len = -1;
                            while ( (len = is.read(fileBuffer)) != -1) {
                                out.write(fileBuffer, 0, len);
                            }
                        } finally {
                            if (is != null) try { is.close(); } catch(IOException ex) {}
                        }
                    }
                    out.write(newLineBytes);
                } // ���� �������� ��� �? ��
                if ( i + 2 == obj.length ) {
                    // ������ Delimeter ���
                    out.write(twoDashBytes);
                    out.write(delimeterBytes);
                    out.write(twoDashBytes);
                    out.write(newLineBytes);
                }
            } // for ������ ��
            
            out.flush();
        } finally {
            if (out != null) out.close();
        }
        return conn.getInputStream();
    }

    /**
     * ������ ArrayList�� ����Ǿ� �ִ� �Ķ����&�� �����
     * application/x-www-form-urlencoded MIME�� ���缭 ���ڵ��Ѵ�.
     * �Ķ������ ���� Ÿ���� File�� ��쿡�� �� �Ķ���͸� �����ϰ�
     * ���� �Ķ���͸� ó���Ѵ�.
     *
     * @param parameters �Ķ���� �̸��� �Ķ���� ���� �����ϰ� �ִ� ��ü
     * @return ���ڵ�� String
     */
    private static String encodeString(ArrayList parameters) {
        StringBuffer sb = new StringBuffer(256);
        
        Object[] obj = new Object[parameters.size()];
        parameters.toArray(obj);
        
        for (int i = 0 ; i < obj.length ; i += 2) {
            if ( obj[i+1] instanceof File || obj[i+1] instanceof NullFile ) continue;
            
            sb.append(URLEncoder.encode((String)obj[i]) );
            sb.append('=');
            sb.append(URLEncoder.encode((String)obj[i+1]) );
            
            if (i + 2 < obj.length) sb.append('&');
        }
        
        return sb.toString();
    }
    
    /**
     * multipart/form-data �� �����͸� ����� �� ���Ǵ�
     * �������͸� ���Ѵ�.
     * <p>
     * ���Ƿ� ������ �ʰ� �Ź� ���� �������͸� ���Ѵ�.
     */
    private static String makeDelimeter() {
        return "---------------------------7d115d2a20060c";
    }
    
    /**
     * ����� ������ �������� ���� ��쿡 ���Ǵ� Ŭ����
     */
    private class NullFile {
        NullFile() {
        }
        public String toString() {
            return "";
        }
    }
    
}

/*
	HttpRequestor Ŭ������ ���

	HttpRequestor Ŭ������ ������� �ſ� �����ϴ�. ������ ���� ��� ������ ����.

    HttpRequestor requestor = new HttpRequestor(someURL);
    requestor.addParameter("param1", "�Ķ����1");
    requestor.addParameter("param2", "parameter2");
    requestor.addFile("file1", null); // ����� ������ �������� �ʴ� ���
    requestor.addFile("file2", new File("c:\\autoexec.bat"));
    
    InputStream is = requestor.sendMultipartPost();
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    ...
    ...
    br.close();
*/